package com.wframe.project.jnd.wx.filter;

import com.frame.database.JdataBean;
import com.google.gson.Gson;
import com.wframe.project.jnd.rest.util.SessionUser;
import com.wframe.project.jnd.rest.util.WebSession;
import com.wframe.project.jnd.services.UserService;
import com.wframe.project.jnd.wx.wxImpl.WeixAccountConfig;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.*;

/**
 * 微信自动登录拦截器
 * <p>
 * 该拦截器将会拦截所有在 /wx/ 下的请求
 * </p>
 * <p>
 * Created by Jin on 2017/4/12.
 */
@Component("wechatFilter")
public class WechatFilter implements Filter {

    private static final String INTERCEPT_PATH = "/we/";

    private static final String WECHAT_STATE_KEY = "WECHAT_STATE_TEMP";

    private static final String WECHAT_INTERCEPT_URL_KEY = "WECHAT_INTERCEPT_URL";

    public static final String WECHAT_USER_KEY = "WECHAT_USER";

    private String codeCallbackUrl;

    private List<String> excludeUrls;

    private WxMpInMemoryConfigStorage wechatConfig;

    private WxMpService wxMpService;

    @Autowired
    private WeixAccountConfig tempWechatConfig;

    @Autowired
    private UserService userService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        List<String> readyExcludeUrls = this.excludeUrls == null ? new ArrayList<String>() : this.excludeUrls;
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        HttpSession session = request.getSession();
        String currentUrl = request.getRequestURI();
        WxMpUser wechatUser = (WxMpUser) session.getAttribute(WECHAT_USER_KEY);

        if (checkNotLogin(session) && (!readyExcludeUrls.contains(currentUrl)) && checkRequest(request)) {
            try {
                if (currentUrl.equalsIgnoreCase(this.codeCallbackUrl)) {
                    //微信返回CODE
                    String callbackCode = request.getParameter("code");
                    String callbackState = request.getParameter("state");
                    String tempState = (String) session.getAttribute(WECHAT_STATE_KEY);
                    String completeUrl = (String) session.getAttribute(WECHAT_INTERCEPT_URL_KEY);
                    if (StringUtils.equalsIgnoreCase(callbackState, tempState)) {
                        session.removeAttribute(WECHAT_STATE_KEY);

                        WxMpOAuth2AccessToken oAuth2AccessToken = this.wxMpService.oauth2getAccessToken(callbackCode);
                        WxMpUser mpUser = this.wxMpService.oauth2getUserInfo(oAuth2AccessToken, null);
                        if (mpUser != null) {
                            session.setAttribute(WECHAT_USER_KEY, mpUser);
                            JdataBean dbWechatUser = this.userService.getUserByWechat(mpUser.getOpenId(), mpUser.getUnionId());
                            if (dbWechatUser != null) {
                                //找到openid或者unionid绑定的用户后自动登录
                                dbWechatUser = this.userService.loginByUser(dbWechatUser, session);
                            }
                        }
                    }
                    session.removeAttribute(WECHAT_INTERCEPT_URL_KEY);
                    response.sendRedirect(completeUrl);
                    return;
                } else {
                    if (wechatUser != null) {

                        session.setAttribute(WECHAT_USER_KEY, wechatUser);
                        JdataBean dbWechatUser = this.userService.getUserByWechat(wechatUser.getOpenId(), wechatUser.getUnionId());
                        if (dbWechatUser != null) {
                            //找到openid或者unionid绑定的用户后自动登录
                            dbWechatUser = this.userService.loginByUser(dbWechatUser, session);
                        }

                        chain.doFilter(servletRequest, servletResponse);
                        return;
                    } else {
                        String requestUrlPrefix = buildRequestUrlPrefix(request);
                        String readyCallbackUrl = requestUrlPrefix + this.codeCallbackUrl;

                        //需要拦截的地址
                        String state = StringUtils.trimToNull(StringUtils.trimToEmpty(UUID.randomUUID().toString()).replace("-", ""));
                        String authorizationUrl = this.wxMpService.oauth2buildAuthorizationUrl(readyCallbackUrl, WxConsts.OAUTH2_SCOPE_USER_INFO, state);
                        String completeCurrentUrl = buildRequestUrl(request);
                        session.setAttribute(WECHAT_STATE_KEY, state);
                        session.setAttribute(WECHAT_INTERCEPT_URL_KEY, completeCurrentUrl);
                        response.sendRedirect(authorizationUrl);
                        return;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();

                String completeUrl = (String) session.getAttribute(WECHAT_INTERCEPT_URL_KEY);
                if (StringUtils.isNotEmpty(completeUrl)) {
                    response.sendRedirect(completeUrl);
                    session.removeAttribute(WECHAT_INTERCEPT_URL_KEY);
                } else {
                    chain.doFilter(servletRequest, servletResponse);
                }

            }
        } else {
            //已登录、不在/we/目录下、指定不包含、非微信、非AJAX、非GET的请求则放过
            chain.doFilter(servletRequest, servletResponse);
        }
    }

    public String buildRequestUrlPrefix(HttpServletRequest request) {
        if (request != null) {
            StringBuffer url = new StringBuffer();
            url.append(request.getScheme());
            url.append("://");
            url.append(request.getServerName());
            if (request.getServerPort() > 0 && ("http".equalsIgnoreCase(request.getScheme()) && request.getServerPort() != 80 || "https".equalsIgnoreCase(request.getScheme()) && request.getServerPort() != 443)) {
                url.append(':');
                url.append(request.getServerPort());
            }
            return url.toString();
        }
        return null;
    }

    /**
     * 拼接包含参数的请求路径
     *
     * @param request
     * @return 完整请求路径
     */
    private String buildRequestUrl(HttpServletRequest request) {
        StringBuffer url = new StringBuffer();
        if (request != null) {
            url = request.getRequestURL();
            Map<String, String[]> paras = request.getParameterMap();
            if (paras != null && paras.size() > 0) {
                String[] keys = paras.keySet().toArray(new String[paras.size()]);
                for (int i = 0; i < keys.length; i++) {
                    String key = keys[i];
                    String[] values = paras.get(key);
                    if (values != null && values.length > 0) {
                        for (int j = 0; j < values.length; j++) {
                            String value = values[j];
                            if (StringUtils.isNotEmpty(key) && StringUtils.isNotEmpty(value)) {
                                if (i == 0 && j == 0) {
                                    url.append("?");
                                } else {
                                    url.append("&");
                                }
                                url.append(key);
                                url.append("=");
                                url.append(value);
                            }
                        }
                    }
                }
            }
        }
        return url.toString();
    }

    /**
     * 检查请求为GET请求、微信环境、非AJAX请求
     *
     * @param request
     * @return true：满足，false：不满足
     */
    private boolean checkRequest(HttpServletRequest request) {
        boolean checkResult = false;
        if (request != null) {
            String userAgent = request.getHeader("User-Agent");
            String requestType = request.getHeader("X-Requested-With");

            if ("GET".equalsIgnoreCase(request.getMethod()) && userAgent != null && userAgent.toLowerCase().lastIndexOf("micromessenger") >= 0 && (requestType == null || (!"XMLHttpRequest".equalsIgnoreCase(requestType)))) {
                //GET请求、微信环境、非AJAX请求
                checkResult = true;
            }

        }
        return checkResult;
    }

    /**
     * 检查用户是否已经登录
     *
     * @param session
     * @return true：未登录，false：已登录
     */
    private boolean checkNotLogin(HttpSession session) {
        SessionUser sessionUser = WebSession.getWbUser(session);
        if (sessionUser != null && StringUtils.isNotEmpty(sessionUser.getUserid())) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 初始化web.xml配置参数，初始化微信
     *
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        this.codeCallbackUrl = StringUtils.trimToNull(filterConfig.getInitParameter("codeCallbackUrl"));
        String[] tempExcludeUrls = StringUtils.trimToEmpty(filterConfig.getInitParameter("excludeUrl")).split(",");
        if (tempExcludeUrls != null && tempExcludeUrls.length > 0) {
            this.excludeUrls = Arrays.asList(tempExcludeUrls);
        }

        this.wechatConfig = new WxMpInMemoryConfigStorage();
        this.wechatConfig.setAppId(this.tempWechatConfig.getAppId());
        this.wechatConfig.setSecret(this.tempWechatConfig.getSecret());
        this.wechatConfig.setToken(this.tempWechatConfig.getToken());
        this.wechatConfig.setAesKey(this.tempWechatConfig.getAesKey());
        this.wxMpService = new WxMpServiceImpl();
        this.wxMpService.setWxMpConfigStorage(this.wechatConfig);

    }

    @Override
    public void destroy() {
    }

}
